package cn.green.mvc.service;

import cn.green.core.components.exceptions.QDdlException;
import cn.green.core.excel.QExcel;
import cn.green.core.excel.QExcelLogger;
import cn.green.core.front.QFront;
import cn.green.core.model.QFieldModel;
import cn.green.core.model.QTableModel;
import cn.green.manage.service.QDdlService;
import cn.green.mvc.dao.OrmTableDao;
import cn.green.mvc.entity.OrmTableEntity;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
  * @className OrmTableService  
  * @author wang.yishu
  * @date   2025/5/29 13:55
  * @version 1.0 
  */
@Transactional(readOnly = true)
public abstract class OrmTableService<T extends OrmTableEntity,D extends OrmTableDao<T>> {
    protected final D dao;
    protected final QFront front;
    protected final QDdlService qDdlService;
    protected final Class<T> entityClass;
    protected final Field[] entityFields;
    protected final QExcel qExcel;
    protected final QExcelLogger qExcelLogger;
    public OrmTableService(D dao, QFront front, QDdlService qDdlService, Class<T> entityClass, QExcel qExcel, QExcelLogger qExcelLogger) {
        this.dao = dao;
        this.front = front;
        this.qDdlService = qDdlService;
        this.entityClass = entityClass;
        this.entityFields = entityClass.getDeclaredFields();
        this.qExcel = qExcel;
        this.qExcelLogger = qExcelLogger;
    }
    public QTableModel getTable() {
        return this.qDdlService.getTable(this.source(), this.scheme(), this.table());
    }
    public QFieldModel getTablePrimaryField() {
        return this.qDdlService.getTablePrimaryField(this.source(), this.scheme(), this.table());
    }
    @Transactional(rollbackFor = Exception.class)
    public int saveField(QFieldModel field) {
        field.setTableName(this.table());
        field.setSourceName(this.source());
        field.setSchemeName(this.scheme());
        boolean exists = this.entityFields != null && this.entityFields.length > 0 && Arrays.stream(this.entityFields).anyMatch(item ->
                item.getName().equals(field.getFieldName()));
        if (this.qDdlService.getField(this.source(), this.scheme(), this.table(), field.getFieldName()) == null) {
            if (exists) {
                throw new QDdlException("The field " + field.getFieldName() + " is already exists");
            }
            return this.qDdlService.addField(field);
        } else {
            if (exists) {
                throw new QDdlException("The entity field " + field.getFieldName() + " can not be updated");
            }
            return this.qDdlService.updField(field, false);
        }
    }
    @Transactional(rollbackFor = Exception.class)
    public int deleteField(QFieldModel field) {
        field.setTableName(this.table());
        field.setSourceName(this.source());
        field.setSchemeName(this.scheme());
        if (this.entityFields != null && this.entityFields.length > 0 && Arrays.stream(this.entityFields).anyMatch(item ->
                item.getName().equals(field.getFieldName()))) {
            throw new QDdlException("The entity field " + field.getFieldName() + " can not be deleted");
        }
        return this.qDdlService.delField(field, false);
    }
    public JSONArray list(JSONObject query) {
        return this.dao.list(query);
    }
    public List<T> list(T param) {
        return this.dao.list(param);
    }
    public JSONArray query(JSONObject query) {
        return this.dao.query(query);
    }
    public JSONObject page(JSONObject query) {
        return this.dao.page(query);
    }
    public Map<String, Object> page(T param) {
        return this.dao.page(param);
    }
    public JSONObject detail(JSONObject query) {
        return this.dao.detail(query);
    }
    public T get(T param) {
        return this.dao.get(param);
    }
    public JSONObject detail(Object primaryVal) {
        return this.dao.detail(primaryVal);
    }
    public T get(Object primaryVal) {
        return this.dao.get(primaryVal);
    }
    public void preSave(JSONObject entity) {}
    public void aftSave(Object saveRes,JSONObject entity) {}
    @Transactional(rollbackFor = Exception.class)
    public Object save(JSONObject entity) {
        this.preSave(entity);
        Object rtn = this.dao.save(entity);
        this.aftSave(rtn, entity);
        return rtn;
    }
    @Transactional(rollbackFor = Exception.class)
    public Object save(T entity) {
        return this.dao.save(entity);
    }
    public void preDelete(JSONObject entity) {}
    public void aftDelete(Object deleteRes,JSONObject entity) {}
    @Transactional(rollbackFor = Exception.class)
    public Object delete(JSONObject entity) {
        this.preDelete(entity);
        Object rtn = this.dao.delete(entity);
        this.aftDelete(rtn, entity);
        return rtn;
    }
    @Transactional(rollbackFor = Exception.class)
    public Object remove(T entity) {
        return this.dao.remove(entity);
    }
    public void preDelete(Object primary) {}
    public void aftDelete(Object deleteRes, Object primary) {}
    @Transactional(rollbackFor = Exception.class)
    public Object delete(Object primary) {
        this.preDelete(primary);
        Object rtn = this.dao.delete(primary);
        this.aftDelete(rtn, primary);
        return rtn;
    }
    @Transactional(rollbackFor = Exception.class)
    public Object remove(Object primary) {
        return this.dao.remove(primary);
    }
    public JSONArray titles() {
        return this.front.buildTitle(this.source(), this.scheme(), this.table());
    }
    public JSONObject insertForm() {
        return this.front.buildFormInsert(this.source(), this.scheme(), this.table());
    }
    public JSONObject updateForm() {
        return this.front.buildFormUpdate(this.source(), this.scheme(), this.table());
    }
    public JSONObject selectForm() {
        return this.front.buildFormSelect(this.source(), this.scheme(), this.table());
    }
    public String source() {
        return this.dao.getSourceName();
    }
    public String scheme() {
        return this.dao.getSchemeName();
    }
    public String table() {
        return this.dao.getTableName();
    }
    public String tableAlias() {
        return this.dao.getTableAlias();
    }
    public void excelTemplateXls(HttpServletResponse response) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(this.tableAlias() + "_template", "UTF8") + ".xls");
        QTableModel table = this.getTable();
        this.qExcel.templateXls(table, response.getOutputStream());
    }
    public void excelTemplateXlsx(HttpServletResponse response) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(this.tableAlias() + "_template", "UTF8") + ".xlsx");
        QTableModel table = this.getTable();
        this.qExcel.templateXlsx(table, response.getOutputStream());
    }
    @Transactional(rollbackFor = Exception.class)
    public int importExcelXls(MultipartFile file) throws IOException {
        QTableModel table = this.getTable();
        this.qExcel.importXlsAndCurd(table, file.getInputStream());
        return 1;
    }
    @Transactional(rollbackFor = Exception.class)
    public int importExcelXlsx(MultipartFile file) throws IOException {
        QTableModel table = this.getTable();
        this.qExcel.importXlsxAndCurd(table, file.getInputStream());
        return 1;
    }
    public void exportExcelXls(JSONObject param, HttpServletResponse response) throws IOException {
        QTableModel table = this.getTable();
        JSONArray list = this.list(param);
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(this.tableAlias() + "_data", "UTF8") + ".xls");
        this.qExcel.exportXls(list, table, response.getOutputStream());
    }
    public void exportExcelXlsx(JSONObject param, HttpServletResponse response) throws IOException {
        QTableModel table = this.getTable();
        JSONArray list = this.list(param);
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(this.tableAlias() + "_data", "UTF8") + ".xlsx");
        this.qExcel.exportXlsx(list, table, response.getOutputStream());
    }
}
